/*
 * @(#)DateTool.java 1.0.2 08/15/2010
 *
 * Copyright 2011 Ice Spring All rights reserved.
 * Mrogrape/Javaliang.com PROPRIETARY.
 */
package com.mrogrape.util;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;

/**
 * 提供一组静态方法以方便的处理日期和时间。
 * 
 * <p>
 * 此类有以下最主要的功能：
 * <ul>
 * <li> <code>Date</code>类型和<code>String</code>类型时间之间的相互转化。
 * <li> 对某个<code>String</code>类型的时间进行格式化输出。
 * <li> 方便的提取时间中的某部分，比如说是年份，月份。
 * <li> 判断闰年，或者是获取某月的天数。
 * </ul>
 * 这些功能已经基本涵盖了日常应用中会遇到的情况，更多的功能将在后续版本中推出。
 * 
 * <p>
 * <b>注意！</b>一些与SUN官方的不同：
 * <ul>
 * <li>1.获取的月份都是从1开始的而不是0，这也就意味着您展示时不再需要+1的步骤，当然也
 * 意味着把这些数字用于数组时您需要-1
 * <li>2.星期的设定不再考虑国家以及地区情况，统一按星期日为每星期第一天，而且其下标为0。
 * </ul>
 * 
 * <p>
 * 本类中使用的Java官方的格式控制符，详情参见Java API:
 * {@link java.text.SimpleDateFormat}类的帮助。
 * 
 * <p>
 * 这里提供一些简单的格式控制符，除了这些字符，其他字母（注意是字母，中文不算）
 * 的存在都会产生异常，而符号在输出的时候都将保持原样。 如果要输出其他字母或单词，
 * 比如“year”这个单词，那么可以将它用单引号引起来即  'year'，如果想输出单引号需要输入
 * 双单引号比如“''”；
 * <ul>
 * <li>y :  年份 yyyy代表完整的年份，yy则代表两位数的年份
 * <li>M：月份 MM代表完整的 两位数的月份 MMM表示的则是完整的月份名称（比如”二月“）
 * <li>d：天份，即<code>DAY_OF_MONTH</code> dd两位数天份
 * <li>H：24小时制的小时数，
 * <li>h：12小时制的小时数 a则表示上午或者是下午
 * <li>m：分钟数 mm代表两位数的分钟数
 * <li>s：秒数
 * <li>S：毫秒数
 * <li>G：公元或者是公元前
 * <li>z：时区
 * <li>Z：时区（这两种时区标准不同）
 * <li>E：星期几
 * </ul>
 * 
 * @author Ice Spring 
 * @version 1.0.2 08/15/10
 * @see java.text.SimpleDateFormat
 * @see java.util.Date
 */
public class DateUtils 
{
	/**
	 * 一个标准的时间格式,<br>需要其他格式可以参考此格式。
	 * <br>输出时间范例:<code> 1970-01-01 公元 00:00:00.000 星期一 CST </code>
	 */
	public static final String STANDARD_DATE_FORMAT = "yyyy-MM-dd G HH:mm:ss.SSS EEE z";
	/**
	 * 一个标准的时间格式,<br>需要其他格式可以参考此格式。
	 * <br>输出时间范例:<code> 1970-01-01 00:00:00 </code>
	 */
	public static final String STANDARD_SIMPLE_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";

	/**
	 * 表示年份。
	 */
	public static final int YEAR = 1 ;
	/**
	 * 表示月份。
	 * <br>与Java官方月份是从0开始的表示形式不同，这里的月份是从1开始的。
	 */
	public static final int MONTH = 2;
	/**
	 * 表示某月中的某天（即年月日中的“日”）。
	 */
	public static final int DAY_OF_MONTH= 3;
	/**
	 * 以24小时制表示的小时数。
	 */
	public static final int HOUR_OF_DAY = 4;
	/**
	 * 以12小时制表示的小时数。
	 */
	public static final int HOUR = 5;
	/**
	 * 表示分钟数。
	 */
	public static final int MINUTE = 6;
	/**
	 * 表示秒数。
	 */
	public static final int SECOND = 7;
	/**
	 * 表述毫秒。
	 */
	public static final int MILLISECOND = 8;
	/**
	 * 表示星期几。
	 */
	public static final int DAY_OF_WEEK = 9;

	/**
	 * 获取当前时间与标准基准时间相差的毫秒数。
	 * 
	 * @return 当前时间与标准基准时间相差的毫秒数。
	 */
	public static long currentTime()
	{
		return new Date().getTime();
	}

	/**
	 * 获取某月的天数。
	 * 
	 * <br>这个方法可以用来很方便的测算二月的天数
	 * @param year 要获取的月所做的年份
	 * @param month 要获取天数的月份
	 * @return <code> int</code>类型 该月的天数
	 */
	public static int daysOfMonth(int year , int month)
	{
		if( (year%4==0&&year%100!=0)||(year%400==0))
		{
			if(month == 2) {
				return 29;
			}
		}
		if(month ==1||month ==3||month ==5||month ==7||month ==8||month ==10||month ==12)
		{
			return 31;
		}
		else if(month == 2) {
			return 28;
		} else {
			return 30;
		}
	}

	/**
	 * 描述一个时间长度，把它转换为类似XXdXXhXXmXXs的形式。
	 * 
	 * @param time 一段长度的时间 不可为负
	 * @param m 是否显示毫秒
	 * @return
	 */
	public static String descTime(long time,boolean m){
		if(time < 0){
			return null;
		}

		String re = "";

		int millisecond = (int) (time%1000);
		int sec = 0;
		int min = 0;
		int hour = 0;
		int day = 0;

		if(time < 1000){//ms

		}else if(time < 60000){//s
			sec = (int) (time/1000);
			re = sec+"s";
		}else if(time < 3600000){//min
			min = (int) ((time/1000)/60);
			sec = (int) (time/1000) - min*60;
			re = min+"m"+sec+"s";
		}else if(time < 1000*60*60*24){
			hour = (int) (((time/1000)/(3600)));
			min = (int) (((time/1000)-hour*3600)/60);
			sec = (int) ((time/1000)-hour*3600 - min*60);
			re = hour+"h"+min+"m"+sec+"s";
		}else{
			day = (int)((time/1000)/(60*60*24));
			hour = (int)(((time/1000)-day*24*3600)/(3600));
			min = (int)(((time/1000)-day*24*3600-hour*3600)/60);
			sec =(int)((time/1000)-day*3600*24-hour*3600-min*60);
			re = day+"d"+hour+"h"+min+"m"+sec+"s";
		}

		if(m)
		{
			re = re+millisecond+"ms";
		}

		return re;
	}

	/**
	 * 判断某年是否是闰年。
	 * 
	 * @param year 需要判断的年份
	 * @return <code>boolean</code>类型 该年份是否是闰年
	 * <br><code>TRUE</code>: 是闰年
	 * <br><code>FALSE</code> : 不是闰年
	 */
	public static boolean isLeapYear(int year)
	{
		if( (year%4==0&&year%100!=0)||(year%400==0))
		{
			return true;
		} else {
			return false;
		}
	}

	/**
	 * 获取当前系统时间的格式化的时间字符串。
	 * 
	 * @param format 需要得到的时间的格式。
	 * @return <code>String</code>类型 格式化的时间字符串。
	 */
	public static String format(String format)
	{
		return new SimpleDateFormat(format).format(new Date());
	}

	/**
	 * 将某个<code>Date</code>对象转化为格式化的时间字符串。
	 * 
	 * @param date 提供的<code>Date</code>对象。
	 * @param format 需要得到的时间的格式。
	 * @return <code>String</code>类型 格式化的时间字符串。
	 */
	public static String format(Date date,String format)
	{
		return new SimpleDateFormat(format).format(date);
	}

	/**
	 * 将<code>标准基准时间以来的毫秒数</code>转化为格式化的时间字符串。
	 * 
	 * @param date 标准基准时间以来的毫秒数。
	 * @param format 需要得到的时间的格式。
	 * @return <code>String</code>类型 格式化的时间字符串。
	 * @since 2.0.0
	 */
	public static String format(long date,String format)
	{
		return new SimpleDateFormat(format).format(new Date(date));
	}

	/**
	 * 根据提供的格式，将某个时间字符串格式化。
	 * 
	 * <br>如果发生异常，将返回原字符串。
	 * @param date 提供的时间字符串。
	 * @param inFormat 提供的时间字符串的格式。
	 * @param outFormat 需要得到的时间字符串的格式。
	 * @return <code>String</code>类型 格式化的时间字符串<br>如果发生异常，将返回原字符串。
	 */
	public static String format(String date,String inFormat, String outFormat)
	{
		SimpleDateFormat sdf = new SimpleDateFormat(inFormat);
		Date datetemp = new Date();
		try {
			datetemp = sdf.parse(date);
			return DateUtils.format(datetemp, outFormat);
		} catch (ParseException e) {
			e.printStackTrace();
		}
		return date;
	}

	/**
	 * 获取当前系统时间的某个部分，如年份，月份等等。
	 * <br>目前可以提取到的部分：年、月、日、时、分、秒、毫秒、星期几。
	 * <br>如果需要提取的部分不存在，则返回0。
	 * @param type 需要获取的部分,这个常量可以从本类中获取。
	 * @return <code>int</code>类型 获取到的部分<br>如果需要提取的部分不存在，则返回0。
	 */
	public static int subTime(int type)
	{
		GregorianCalendar gc = new GregorianCalendar();
		switch(type)
		{
		case DateUtils.YEAR:
			return gc.get(Calendar.YEAR);
		case DateUtils.MONTH:
			return gc.get(Calendar.MONTH)+1;
		case DateUtils.DAY_OF_MONTH:
			return gc.get(Calendar.DAY_OF_MONTH);
		case DateUtils.HOUR:
			return gc.get(Calendar.HOUR);
		case DateUtils.HOUR_OF_DAY:
			return gc.get(Calendar.HOUR_OF_DAY);
		case DateUtils.MINUTE:
			return gc.get(Calendar.MINUTE);
		case DateUtils.SECOND:
			return gc.get(Calendar.SECOND);
		case DateUtils.MILLISECOND:
			return gc.get(Calendar.MILLISECOND);
		case DateUtils.DAY_OF_WEEK:
			return ((gc.get(Calendar.DAY_OF_WEEK))-1);
		default : return 0;
		}
	}

	/**
	 * 获取指定的<code>Date</code>对象的某部分时间。
	 * <br> 可以提取的部分有 年、月、日、时、分、秒、毫秒、星期几。
	 * @param date 指定的<code>Date</code>对象
	 * @param type 需要提取的部分
	 * @return <code>int</code>类型 提取出的部分<br> 如果需要提取的部分不被支持，则返回0
	 */
	public static int subTime(Date date,int type)
	{
		GregorianCalendar gc = DateUtils.toGregorianCalendar(date);
		switch(type)
		{
		case DateUtils.YEAR:
			return gc.get(Calendar.YEAR);
		case DateUtils.MONTH:
			return (gc.get(Calendar.MONTH))+1;
		case DateUtils.DAY_OF_MONTH:
			return gc.get(Calendar.DAY_OF_MONTH);
		case DateUtils.HOUR:
			return gc.get(Calendar.HOUR);
		case DateUtils.HOUR_OF_DAY:
			return gc.get(Calendar.HOUR_OF_DAY);
		case DateUtils.MINUTE:
			return gc.get(Calendar.MINUTE);
		case DateUtils.SECOND:
			return gc.get(Calendar.SECOND);
		case DateUtils.DAY_OF_WEEK:
			return (gc.get(Calendar.DAY_OF_WEEK)-1);
		case DateUtils.MILLISECOND:
			return (int)(date.getTime() - DateUtils.toDate(DateUtils.format(date, "yyyy-MM-dd HH-mm-ss"), "yyyy-MM-dd HH-mm-ss").getTime());
		default : return 0;
		}
	}

	/**
	 * 提取给定的时间字符串的某个部分。
	 * 
	 * <br>可以提取到的部分有：年，月，日，时，分，秒，毫秒，星期几。
	 * @param date 需要提取的时间字符串
	 * @param format 提供的时间字符串的格式
	 * @param type 需要提取的部分
	 * @return <code>int</code>类型 提取到的某部分<br>注意！如果提供的时间字符串没有包含要提取的部分或提取的部分不被支持，方法将返回0
	 */
	public static int subTime(String date,String format,int type)
	{
		switch(type)
		{
		case DateUtils.YEAR:
			return Integer.parseInt(DateUtils.format(date, format, "yyyy"));
		case DateUtils.MONTH:
			return Integer.parseInt(DateUtils.format(date, format, "MM"));
		case DateUtils.DAY_OF_MONTH:
			return Integer.parseInt(DateUtils.format(date, format, "dd"));
		case DateUtils.HOUR_OF_DAY:
			return Integer.parseInt(DateUtils.format(date, format, "HH"));
		case DateUtils.HOUR:
			return Integer.parseInt(DateUtils.format(date, format, "hh"));
		case DateUtils.MINUTE:
			return Integer.parseInt(DateUtils.format(date, format, "mm"));
		case DateUtils.SECOND:
			return Integer.parseInt(DateUtils.format(date, format, "ss"));
		case DateUtils.MILLISECOND:
			return Integer.parseInt(DateUtils.format(date, format, "SSS"));
		case DateUtils.DAY_OF_WEEK:
			return  DateUtils.toGreGorianCalendar(date, format).get(Calendar.DAY_OF_WEEK)-1;
		default :
			return 0;

		}
	}

	/**
	 * 获取以当前系统时间为基准一个时间段后的<code>Date</code>对象。
	 * 
	 * @param time 时间段长度
	 * @return 该时间段之后的<code>Date</code>对象
	 */
	public static Date theDateAfter(long time)
	{
		long date = new Date().getTime()+time;
		return new Date(date);
	}

	/**
	 * 获取一个时间点经过固定时间段后的时间对象。
	 * 
	 * @param begin 起始时间点对象
	 * @param time 经历的时间长度
	 * @return
	 */
	public static Date theDateAfter(Date begin , long time)
	{
		return new Date(begin.getTime()+time);
	}

	/**
	 * 获取当前系统时间到某个以后的时间点相差的时间。
	 * 
	 * 注意：如果输入的时间在系统时间之前，则会返回0
	 * @param end 一个当前系统时间以后的时间点
	 * @return 当前系统时间到输入时间点剩余时间的毫秒数
	 */
	public static long timeRemain(Date end)
	{
		Date dt = new Date();
		if(end.after(dt))
		{
			return end.getTime() - dt.getTime();
		}
		return 0;
	}

	/**
	 * 获取当前系统时间到某个以后的时间点相差的时间。
	 * 
	 * @param end 一个当前系统时间以后的时间点
	 * @param format 该时间的格式
	 * @return 当前系统时间到输入时间点剩余时间的毫秒数
	 */
	public static long timeRemain(String end , String format)
	{
		Date dtEnd = DateUtils.toDate(end, format);
		Date dtNow = new Date();
		if(dtEnd.after(dtNow))
		{
			return dtEnd.getTime() - dtNow.getTime();
		}
		return 0;
	}

	/**
	 * 获取两个时间点之间相差的时间。
	 * 
	 * @param begin 起始时间
	 * @param end 结束时间
	 * @return 相差的时间的毫秒数
	 */
	public static long timeRemain(Date begin ,Date end)
	{
		if(end.after(begin))
		{
			return end.getTime() - begin.getTime();
		}
		return 0;
	}

	/**
	 * 获取两个时间点之间相差的时间。
	 * 
	 * @param begin 起始时间字符串
	 * @param end 结束时间字符串
	 * @param format 时间字符串的格式
	 * @return 相差的时间的毫秒数
	 */
	public static long timeRemain(String begin , String end ,String format)
	{
		Date dtEnd = DateUtils.toDate(end, format);
		Date dtBegin = DateUtils.toDate(begin, format);
		if(dtEnd.after(dtBegin))
		{
			return dtEnd.getTime() - dtBegin.getTime();
		}
		return 0;
	}

	/**
	 * 根据提供的格式，将一个<code>String</code>类型的时间转化为一个<code>Date</code>对象。
	 * <br>如果发生异常，将返回以当前系统时间创建的默认<code>Date</code>对象。
	 * @param date <code>String</code>类型的时间。
	 * @param format <code>String</code>类型的时间的格式。
	 * @return 转换得到的<code>Date</code>对象<br>如果发生异常，将返回以当前系统时间创建的默认Date对象。
	 */
	public static Date toDate(String date,String format)
	{
		SimpleDateFormat sf = new SimpleDateFormat(format);
		try {
			return sf.parse(date);
		} catch (ParseException e) {
			e.printStackTrace();
		}
		return new Date();
	}

	/**
	 * 将一个<code>标准时间</code>转化为一个<code>GregorianCalendar</code>对象。
	 * <br>这是一个非常有用的方法，因为我们常常需要调用一
	 * 个<code>GregorianCalendar</code>对象的某些方法，
	 * 但是<code>GregorianCalendar</code>没有提供直接转
	 * 换。通过这个方法可以快速实现转化。这
	 * 样就可以使用<code>GregorianCalendar</code>的很多
	 * 方法了
	 * <br>警告！该方法转换的时候会损失精度,转换后将无法保留毫秒部分
	 * <br>如果在转换的过程中出现异常，这个方法返回的将是默认的，以当前系统时间创建的<code>GregorianCalendar</code>对象
	 * @param date 提供的<code>从标准时间以来的毫秒数</code>
	 * @return 转换得到的GregorianCalendar对象<br>如果失败则返回以当前系统时间创建的<code>GregorianCalendar</code>对象
	 * @see JavaAPI java.util.GregorianCalendar
	 * @since 2.0.0
	 */
	public static GregorianCalendar toGregorianCalendar(long date)
	{
		try{
			return new GregorianCalendar(
					Integer.parseInt(DateUtils.format(date, "yyyy")),
					Integer.parseInt(DateUtils.format(date, "MM")),
					Integer.parseInt(DateUtils.format(date, "dd")),
					Integer.parseInt(DateUtils.format(date, "HH")),
					Integer.parseInt(DateUtils.format(date, "mm")),
					Integer.parseInt(DateUtils.format(date, "ss"))
			);
		}
		catch(Exception e)
		{
			e.printStackTrace();
			return new GregorianCalendar();
		}
	}

	/**
	 * 将一个<code>Date</code>对象转化为一个<code>GregorianCalendar</code>对象。
	 * 
	 * <br>这是一个非常有用的方法，因为我们常常需要调用一
	 * 个<code>GregorianCalendar</code>对象的某些方法，
	 * 但是<code>GregorianCalendar</code>没有提供直接转
	 * 换。通过这个方法可以快速实现转化。这
	 * 样就可以使用<code>GregorianCalendar</code>的很多
	 * 方法了
	 * <br>警告！该方法转换的时候会损失精度,转换后将无法保留毫秒部分
	 * <br>如果在转换的过程中出现异常，这个方法返回的将是默认的，以当前系统时间创建的<code>GregorianCalendar</code>对象
	 * @param date 提供的<code>Date<c/ode>对象
	 * @return 转换得到的GregorianCalendar对象<br>如果失败则返回以当前系统时间创建的<code>GregorianCalendar</code>对象
	 * @see JavaAPI java.util.GregorianCalendar
	 */
	public static GregorianCalendar toGregorianCalendar(Date date)
	{
		try{
			return new GregorianCalendar(
					Integer.parseInt(DateUtils.format(date, "yyyy")),
					Integer.parseInt(DateUtils.format(date, "MM")),
					Integer.parseInt(DateUtils.format(date, "dd")),
					Integer.parseInt(DateUtils.format(date, "HH")),
					Integer.parseInt(DateUtils.format(date, "mm")),
					Integer.parseInt(DateUtils.format(date, "ss"))
			);
		}
		catch(Exception e)
		{
			e.printStackTrace();
			return new GregorianCalendar();
		}
	}

	/**
	 * 根据指定的时间字符串和格式，获得一个<code>GregorianCalendar</code>对象。
	 * 
	 * @param date 指定的时间字符串
	 * @param format 指定的时间字符串的格式
	 * @return 创建的<code>GregorianCalendar</code>对象
	 * @see JavaAPI java.util.GregorianCalendar
	 */
	public static GregorianCalendar toGreGorianCalendar(String date,String format)
	{
		return  new GregorianCalendar(DateUtils.subTime(date, format, DateUtils.YEAR),
				DateUtils.subTime(date, format, DateUtils.MONTH)-1,
				DateUtils.subTime(date, format, DateUtils.DAY_OF_MONTH),
				DateUtils.subTime(date, format, DateUtils.HOUR_OF_DAY),
				DateUtils.subTime(date, format, DateUtils.MINUTE),
				DateUtils.subTime(date, format, DateUtils.SECOND));
	}

}
